package com.bestcem.xm.user.handler.excel.read.listener;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.util.ConverterUtils;
import com.bestcem.xm.user.constant.BusinessConstant;
import com.bestcem.xm.user.enums.GroupImportErrorEnum;
import com.bestcem.xm.user.grpc.service.param.GroupLevelSyncParam;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.*;

/**
 * 开放平台的层级更新sheet
 *
 * @author wangyunyun <yunyun.wang@idiaoyan.com>
 * @version v1.0
 * @date 2022/4/21 15:47
 */
@Getter
@Setter
@Slf4j
public class OpenLevelReadListener extends AnalysisEventListener<GroupLevelSyncParam> {

    private Map<Integer, String> headers;

    private List<GroupLevelSyncParam> dataList = new ArrayList<>();

    private Integer errorStatus;

    private String errorMsg;

    private boolean hasNext = true;

    private boolean hasDataError = false;

    private String sheetName;

    private Integer totalGroupCount = 0;

    private Set<String> groupCodes = new HashSet<>();

    /**
     * 这里会一行行的返回头
     *
     * @param headMap
     * @param context
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/2/8 17:47
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        //  转换成 Map<Integer,String>
        this.headers = ConverterUtils.convertToStringMap(headMap, context);

        // 校验表头格式
        checkHeaders();
    }

    /**
     * 每读到一行数据都调用invoke方法
     *
     * @param data 行数据
     * @param context 表格context
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/2/8 17:47
     */
    @Override
    public void invoke(GroupLevelSyncParam data, AnalysisContext context) {

        // 校验行数据
        checkRows();

        // 把数据放到dataList里面，便于统一处理
        this.dataList.add(data);
    }

    /**
     * 读完sheet的处理
     *
     * @param context 表格context
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:28
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 读完所有数据，做统一处理。
    }

    /**
     * 是否需要继续读表格下一行数据
     *
     * @param context 表格context
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:28
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        return this.hasNext && this.errorStatus == null && !this.hasDataError;
    }

    /**
     * sheet格式是否有错误
     *
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:29
     */
    public boolean hasReadError() {
        return this.errorStatus != null;
    }

    /**
     * sheet数据格式是否有错误
     *
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:30
     */
    public boolean hasDataError() {
        return this.hasDataError;
    }

    /**
     * 校验表头是否正确
     *
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:30
     */
    private boolean checkHeaders() {
        // 获取规定的表头
        Map<Integer, String> formatHeaders = null;
        try {
            formatHeaders = this.getIndexNameMap();
        } catch (NoSuchFieldException e) {
            return doWholeError(GroupImportErrorEnum.HEAD_PARSE_OBJECT_ERROR, this.sheetName);
        }

        // 校验表头
        for (Map.Entry<Integer, String> entry : this.headers.entrySet()) {
            Integer key = entry.getKey();
            // 校验表头之间是否有空列
            if (StringUtils.isBlank(this.headers.get(key))) {
                return doWholeError(GroupImportErrorEnum.HEAD_BLANK_ERROR, this.sheetName);
            }

            // 表头与规定的表头不一致
            if (!this.headers.get(key).equals(formatHeaders.get(key))) {
                return doWholeError(GroupImportErrorEnum.HEAD_FORMAT_ERROR, this.sheetName);
            }
        }

        return true;
    }

    /**
     * 校验行数据是否正确
     *
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:30
     */
    private boolean checkRows() {
        // 校验组织数量
        this.totalGroupCount ++;
        if (BusinessConstant.USER_LEVEL_MAX_COUNT < this.totalGroupCount) {
            doWholeError(GroupImportErrorEnum.LEVEL_MAX_ERROR, BusinessConstant.USER_LEVEL_MAX_COUNT);
        }

        return true;
    }

    /**
     * 通过class获取类字段信息
     *
     * @return Map<Integer, String>
     * @throws NoSuchFieldException
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private Map<Integer, String> getIndexNameMap() throws NoSuchFieldException {
        Map<Integer, String> result = new HashMap<>();
        Field field;
        // 获取类中所有的属性
        Field[] fields = GroupLevelSyncParam.class.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            field = GroupLevelSyncParam.class.getDeclaredField(fields[i].getName());
            field.setAccessible(true);
            // 获取根据注解的方式获取ExcelProperty修饰的字段
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null) {
                // 索引值
                int index = excelProperty.index();
                // 字段值
                String[] values = excelProperty.value();
                StringBuilder value = new StringBuilder();
                for (String v : values) {
                    value.append(v);
                }
                result.put(index, value.toString());
            }
        }
        return result;
    }

    /**
     * 处理错误信息: excel文件格式错误
     *
     * @param errorEnum 错误枚举
     * @param arguments 错误信息参数数组
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private boolean doWholeError(GroupImportErrorEnum errorEnum, Object ... arguments) {
        this.errorStatus = errorEnum.getStatus();
        this.errorMsg = MessageFormat.format(errorEnum.getMsg(), arguments);

        return false;
    }

}
